/*******************************************************************************
 * Copyright (c) 2011 Prometheus Consulting
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *******************************************************************************/

package nz.co.senanque.vaadinsupport.viewmanager;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.text.MessageFormat;

import javax.security.auth.login.LoginException;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.context.MessageSourceAware;
import org.springframework.context.support.MessageSourceAccessor;

import com.vaadin.terminal.gwt.client.ApplicationConnection;
import com.vaadin.ui.AbstractLayout;
import com.vaadin.ui.Alignment;
import com.vaadin.ui.LoginForm;
import com.vaadin.ui.LoginForm.LoginEvent;
import com.vaadin.ui.LoginForm.LoginListener;
import com.vaadin.ui.Panel;
import com.vaadin.ui.VerticalLayout;
import com.vaadin.ui.Window.Notification;


/**
 * 
 * Adapted from http://dev.vaadin.com/browser/incubator/gasdiary/src/org/vaadin/gasdiary/ui/LoginScreen.java
 * but that probably does it better long term (allows creation of new user-this doesn't).
 * However this allows you to define the login fragment (login.html) in your theme directory
 * and it calls an external (injected) authentication mechanism.
 * Also nice support for locale.
 * 
 * @author Roger Parkinson
 * @version $Revision:$
 */

public class LoginLayout extends VerticalLayout implements InitializingBean,ViewManaged, MessageSourceAware
{
    private static final long serialVersionUID = 1L;
    private static Logger logger = LoggerFactory.getLogger(LoginLayout.class);
    
    @Autowired private LoginListener m_loginListener;
    private ViewManager m_viewManager;
	private MessageSourceAccessor m_messageSourceAccessor;
    
    public class LoginFormI18n extends LoginForm
    {
		private static final long serialVersionUID = 8927668036479913166L;
		String usernameCaption;
        String passwordCaption;
        String submitCaption;
        String welcomeCaption;
        
        public LoginFormI18n(String usernameCaption,
                String passwordCaption, String submitCaption, String welcomeCaption) {
            this.usernameCaption = usernameCaption;
            this.passwordCaption = passwordCaption;
            this.submitCaption  = submitCaption;
            this.welcomeCaption  = welcomeCaption;
        }
        
        protected byte[] getLoginHTML() {
               
               String x = "<!DOCTYPE html PUBLIC \"-//W3C//DTD "
                      + "XHTML 1.0 Transitional//EN\" "
                      + "\"http://www.w3.org/TR/xhtml1/"
                      + "DTD/xhtml1-transitional.dtd\">\n";

                String appUri = getApplication().getURL().toString()
                        + getWindow().getName() + "/";
                
                String h = "<head><script type='text/javascript'>"
                    + "var setTarget = function() {"
                    + "  var uri = '" + appUri + "loginHandler';"
                    + "  var f = document.getElementById('loginf');"
                    + "  document.forms[0].action = uri;"
                    + "  document.forms[0].username.focus();"
                    + "};"
                    + ""
                    + "var styles = window.parent.document.styleSheets;"
                    + "for(var j = 0; j < styles.length; j++) {\n"
                    + "  if(styles[j].href) {"
                    + "    var stylesheet = document.createElement('link');\n"
                    + "    stylesheet.setAttribute('rel', 'stylesheet');\n"
                    + "    stylesheet.setAttribute('type', 'text/css');\n"
                    + "    stylesheet.setAttribute('href', styles[j].href);\n"
                    + "    document.getElementsByTagName('head')[0]"
                    + "                .appendChild(stylesheet);\n"
                    + "  }"
                    + "}\n"
                    + "function submitOnEnter(e) {"
                    + "  var keycode = e.keyCode || e.which;"
                    + "  if (keycode == 13) {document.forms[0].submit();}"
                    + "}\n"
                    + "</script>"
                    + "</head>";
                
                String b = "<body onload='setTarget();'"
                    + "  style='margin:0;padding:0; background:transparent;'"
                    + "  class='"
                    + ApplicationConnection.GENERATED_BODY_CLASSNAME + "'>"
                    + "<div class='v-app v-app-loginpage'"
                    + "     style='background:transparent;'>"
                    + "<iframe name='logintarget' style='width:0;height:0;"
                    + "border:0;margin:0;padding:0;'></iframe>"
                    + "<form id='loginf' target='logintarget'"
                    + "      onkeypress='submitOnEnter(event)'"
                    + "      method='post'>";

                String theme = getApplication().getTheme();
                File baseDirectory = getApplication().getContext().getBaseDirectory();
                InputStream is = null;
                if (baseDirectory.isDirectory())
                {
                	String loginLocation = baseDirectory.getPath()+"/VAADIN/themes/"+theme+"/login.html";
                	try {
						is = new FileInputStream(loginLocation);
					} catch (FileNotFoundException e) {
						logger.warn("Failed to open login.html: {} falling back to default version",loginLocation);
						is=null;
					}
                }
                if (is == null)
                {
                	is = this.getClass().getResourceAsStream("login.html");
                }
                StringBuilder sb = new StringBuilder();
                while (true)
                {
                    byte[] bytes = new byte[1000];
                    int i=0;
                    try {
                        i = is.read(bytes);
                    } catch (IOException e) {
                        throw new RuntimeException("Failed to open login.html");
                    }
                    if (i == -1)
                    {
                        break;
                    }
                    sb.append(new String(bytes,0,i));
                }
                String c = MessageFormat.format(sb.toString(),welcomeCaption, usernameCaption, passwordCaption, submitCaption);
                return (x + "<html>" + h + b + c + "</form></html>").getBytes();
        }     
    }

    public LoginLayout()
    {
        
    }
    public void afterPropertiesSet() throws Exception 
    {
        setSizeFull();
        Panel loginPanel = new Panel();
        AbstractLayout panelLayout = (AbstractLayout) loginPanel.getContent();
        panelLayout.setMargin(false);
        loginPanel.setWidth("460px");
        final LoginForm loginForm = new LoginFormI18n(
        		m_messageSourceAccessor.getMessage("username"),
        		m_messageSourceAccessor.getMessage("password"),
        		m_messageSourceAccessor.getMessage("login.button"),
        		m_messageSourceAccessor.getMessage("welcome"));
        loginForm.setHeight("300px");
        loginForm.addListener(new LoginListener(){

            private static final long serialVersionUID = 5201900702970450254L;

			public void onLogin(LoginEvent event)
            {
                if (getLoginListener() != null)
                {
                    try
                    {
                        getLoginListener().onLogin(event);
                    }
                    catch (Exception e)
                    {
                        Throwable cause = e.getCause();
                        if (cause == null || !(cause instanceof LoginException))
                        {
                            logger.error(e.getMessage(),e);
                        }
                        String message = m_messageSourceAccessor.getMessage("Bad.Login", "Bad Login");
                        getViewManager().getMainWindow().showNotification(message,Notification.TYPE_ERROR_MESSAGE);
                        return;
                    }
                }
                getViewManager().loadApplication();
            }});
        loginPanel.addComponent(loginForm);
        addComponent(loginPanel);
        setComponentAlignment(loginPanel, Alignment.MIDDLE_CENTER);
    }
    public LoginListener getLoginListener()
    {
        return m_loginListener;
    }
    public void setLoginListener(LoginListener loginListener)
    {
        m_loginListener = loginListener;
    }
    public void switchedTo()
    {
        // TODO Auto-generated method stub        
    }
	public void setMessageSource(MessageSource messageSource) {
		m_messageSourceAccessor = new MessageSourceAccessor(messageSource);		
	}
	public ViewManager getViewManager() {
		return m_viewManager;
	}
	public void setViewManager(ViewManager viewManager) {
		m_viewManager = viewManager;
	}

}
